
#include "learn_mac_cfg.h"

extern mac_table *total_mac_table[MAX_NODE_NUM];

void total_mac_table_resouce_clear()
{
	int i = 0;

	for (i = 0; i < MAX_NODE_NUM; i++)
	{
		if (total_mac_table[i])
		{
			free(total_mac_table[i]);
			total_mac_table[i] = NULL;
		}
	}

	return;
}

// 判断地址是否相等，相等返回0，不相等返回非0
int ether_addr_equal(u8 *addr1, u8 *addr2)
{
	u16 *a = (u16 *)addr1;
	u16 *b = (u16 *)addr2;

	return ((a[0] ^ b[0]) | (a[1] ^ b[1]) | (a[2] ^ b[2])) != 0;
}

// 判断MAC表是否已存在当前MAC地址，存在返回表项索引，不存在返回-1
int ether_exist_mac_entry(u8 *smac, u16 node_id)
{
	int i = 0;
	int find_flag = 0;
	int ret = -1;

	// 只考虑Mac自学习的表项更新

	for (i = total_mac_table[node_id]->static_size; i < total_mac_table[node_id]->total_size; i++)
	{
		if (total_mac_table[node_id]->table[i].valid == 1) /*MAC地址有效*/
		{
			if (!ether_addr_equal(smac, total_mac_table[node_id]->table[i].mac))
			{
				/*当前存储空间地址与输入学习的地址相同，则更新此输入端口号*/
				find_flag = 1;
				break;
			}
		}
		else
		{
			continue;
		}
	}

	if (find_flag == 1)
	{
		ret = i;
	}

	return ret;
}

int cfg_mac(u8 *pkt, u16 node_id, u8 entry_id)
{
	u8 *data = NULL;
	u16 data_num = 0;
	u16 outport = 0;

	u32 addr = 0; // 配置的地址
	int ret = 0;

	u8 local_mac[6] = {0};

	// data内容直接放到pkt中，省去再次赋值
	data = (u8 *)(pkt + sizeof(tsmp_header) + sizeof(tsmp_set_req_or_get_res_pkt_data)); // 配置的数据

	addr = MAC_TABLE + 2 * entry_id;
	data_num = 2; // mac转发表表项格式为valid(1bit)+pad(6bit)+outport(9bit) + mac(48bit)，共64bit，硬件需要两个32bit存储

	get_hcp_mac_from_mid(local_mac, node_id);

	outport = total_mac_table[node_id]->table[entry_id].port;
	printf("outport： %x \n", outport);

	outport = outport | 0x8000;
	outport = htons(outport);
	memcpy(&data[0], (u8 *)&outport, 2);

	memcpy(&data[2], (u8 *)&total_mac_table[node_id]->table[entry_id].mac, 6);

	tsmp_set_req(local_mac, data_num, htonl(addr), pkt); // 写请求

#if 0
    //配置验证
    ret = cfg_varify(local_mac,data_num,htonl(addr),pkt);

    //配置验证失败，程序退出
	if(ret == -1)
	{
		return -1;
	}
#endif

	return 0;
}

u16 get_table_aging_entry_id(u8 *pkt, u8 *local_mac, u16 node_id)
{
	u16 data_num = 0;

	u32 addr = 0; // 配置的地址
	int i = 0;

	u16 min_cnt = 0;
	u8 min_entry_id = 0;

	u16 hit_cnt[MAX_MAC_ENTRY_NUM];
	u32 *data = NULL;

	addr = ENTRY_HIT_CNT_BASE_ADDR;
	data_num = MAX_MAC_ENTRY_NUM / 2; // mac转发表每条表项计数器为16bit，一个32bit存储两条表项计数器值

	tsmp_get_req(local_mac, data_num, htonl(addr), pkt); // 读请求

	data = tsmp_get_res(local_mac, data_num, htonl(addr)); // 读响应

	memcpy((u8 *)hit_cnt, (u8 *)data, 2 * MAX_MAC_ENTRY_NUM); // 每个计数器2个字节

	min_entry_id = total_mac_table[node_id]->static_size;
	min_cnt = hit_cnt[min_entry_id];
	for (i = total_mac_table[node_id]->static_size + 1; i < total_mac_table[node_id]->total_size; i++)
	{
		if (hit_cnt[i] < min_cnt)
		{
			min_cnt = hit_cnt[i];
			min_entry_id = i;
		}
	}

	// 清空pkt
	bzero(pkt, 2048);

	return min_entry_id;
}

u8 mac_table_aging_cfg(u8 *pkt, u16 node_id)
{

	u8 entry_id = 0;
	u8 local_mac[6] = {0};
	u32 *data = NULL;

	get_hcp_mac_from_mid(local_mac, node_id);
	entry_id = get_table_aging_entry_id(pkt, local_mac, node_id);

	// 清空计数器
	bzero(pkt, 2048);
	data = (u32 *)(pkt + sizeof(tsmp_header) + sizeof(tsmp_set_req_or_get_res_pkt_data));
	data[0] = htonl(1);
	tsmp_set_req(local_mac, 1, htonl(HIT_CNT_CLR), pkt); // 写请求

	return entry_id;
}

int learn_mac_cfg()
{
	u16 pkt_len = 1; // 报文长度
	u8 *pkt = NULL;	 // 报文的指针

	u8 *cfg_pkt = NULL; // 报文的指针
	u8 *tmp_pkt = NULL;

	u16 node_id = 0;
	u8 entry_id = 0;
	int ret = -1;
	u32 outport = 0;

	cfg_pkt = (u8 *)malloc(MAX_PKT_LEN);
	if (NULL == cfg_pkt)
	{
		printf("learn_mac_cfg,malloc buf fail\n");
		return -1;
	}

	bzero(cfg_pkt, MAX_PKT_LEN);

	while (1)
	{

		// 每次获取一个报文
		pkt = tsmp_pkt_receive_dispatch_1(&pkt_len);

		if (pkt == NULL)
			continue;
		else
		{
			// 获取设备节点的Mid
			tmp_pkt = pkt + 6; // TSMP头SMAC字段标识HCP的MID
			node_id = get_mid_from_mac(tmp_pkt);

			// 设备节点mac表项不存在，第一次配置

			if (total_mac_table[node_id] == NULL)
			{
				total_mac_table[node_id] = (mac_table *)malloc(sizeof(mac_table));
				memset(total_mac_table[node_id], 0, sizeof(mac_table));

				entry_id = total_mac_table[node_id]->total_size;
				total_mac_table[node_id]->mid = node_id;

				total_mac_table[node_id]->total_size = entry_id + 1;
			}
			else
			{
				// 设备节点mac表项已存在，非第一次配置

				tmp_pkt = pkt + 16 + 6; // 去掉TSMP头（16字节）后原以太网报文的SMAC

				// 判读是做更新表项处理，还是新增表项处理
				ret = ether_exist_mac_entry(tmp_pkt, node_id);

				// 替换更新表项
				if (ret != -1)
				{
					entry_id = ret;
					printf("exist_mac_entry，node_id=%d ,entry_id = %d\n", node_id, entry_id);
				}
				else
				{
					// 新增表项
					// 表项满了，需要老化表项
					if (total_mac_table[node_id]->total_size >= MAX_MAC_ENTRY_NUM)
					{
						printf("total_mac_table[%d]->total_size = %d > MAX_MAC_ENTRY_NUM \n", node_id, total_mac_table[node_id]->total_size);

						entry_id = mac_table_aging_cfg(cfg_pkt, node_id);
					}
					else
					{
						// 表项未满，直接新增表项
						entry_id = total_mac_table[node_id]->total_size;

						total_mac_table[node_id]->total_size = entry_id + 1;
					}
				}
			}

			total_mac_table[node_id]->table[entry_id].valid = 1;
			// outport = pkt[21];端口号现已移至最高8bit
			outport = pkt[16];
			outport = 1 << outport;
			total_mac_table[node_id]->table[entry_id].port = outport;
			memcpy(total_mac_table[node_id]->table[entry_id].mac, tmp_pkt, 6);

			printf("cfg_mac，node_id=%d ,entry_id = %d\n", node_id, entry_id);
			cfg_mac(cfg_pkt, node_id, entry_id);
		}
	}

	free(cfg_pkt);
	total_mac_table_resouce_clear();

	return 0;
}
